#include<iostream>
#include<cstdlib>
#include<string.h>
#include<cstdio>
#include<cstring>
#include <sys/types.h>
#include<sys/wait.h>
#include<unistd.h>

  //shell的全局定义
  // 1. 命令行参数表
  #define MAXARGC 128
  char* g_argv[MAXARGC];
  int g_argc = 0;

  // 2. 环境变量表
  #define MAX_ENVS 100
  char *g_env[MAX_ENVS];
  int g_envs = 0;


  //last 退出码
  int lastnode = 0;


  // for test
	char cwd[1024];
	char cwdenv[1024];

const char* GetHome()
{
  const char* home = getenv("HOME");
  return home == NULL ? "" : home;
}

bool Cd()
{
  if(g_argc == 1)
  {
    std::string home = GetHome();
    if(home.empty()) return true;
    chdir(home.c_str());
  }
  else{
    std::string where = g_argv[1];
    if(where == "~")
    {
      //chdir();
    }
    else if(where == "-")
    {
      //chdir();
    }
    else 
    {
      chdir(where.c_str());
    }
  }
  return true;
}



const char* GetUserName()
{
  const char* name = getenv("USER");
  return name == NULL ? "None" : name;
}

const char* GetHostName()
{
  const char* hostname = getenv("HOSTNAME");
  return hostname == NULL ? "None" : hostname;
}

const char* GetPWD()
{
  //const char* pwd = getenv("PWD");
  const char* pwd = getcwd(cwd, sizeof(cwd));
  if(pwd != NULL)
  {
		snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
		putenv(cwdenv);	
  }
  return pwd == NULL ? "None" : pwd;
}

std::string DirName(const char* pwd)
{
#define SLASH "/"
  std::string dir = pwd;
  if(dir == SLASH) return SLASH;
  auto pos = dir.rfind(SLASH);
  if(pos == std::string::npos) return "BUGS";

  return dir.substr(pos + 1);
}


  #define COMMAND_SIZE 1024
  #define FORMAT "[%s@%s %s]"

void MakeCommandLine(char cmd_prompt[], int size)
{
  snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPWD()).c_str());
}

void PrintCommdPrompt()
{
  
  char prompt[COMMAND_SIZE];
  MakeCommandLine(prompt, sizeof(prompt));
 //snprintf(prompt, COMMAND_SIZE, FORMAT, GetUserName(), GetHostName(), GetPWD());
  printf("%s", prompt);
  fflush(stdout);
}


bool GetCommandLine(char* out, int size)
{
  // ls -a -l => "ls -a -l\n" 字符串
  char* c = fgets(out, size, stdin);   //很多系统调用都是用c语言做接口，getline等其他c++接口也能用，但最好用c语言
  if(c == NULL) return false;
  
  out[strlen(out) - 1] = 0; //清理\n   ????????
  if(strlen(out) == 0) return false;

  return true;
}


bool CommandParse(char* commandline)
{

  #define SEP " "
// 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
  g_argc = 0;
  g_argv[g_argc++] = strtok(commandline, SEP);

  while((bool)(g_argv[g_argc++] = strtok(NULL, SEP)));

  g_argc--;
  return g_argc > 0 ? true : false;

}

//void PrintArgv()
//{
//    //for(int i = 0; g_argv[i]; i++)
//    for(int i = 0; i < g_argc; i++)
//    {
//        printf("argv[%d]->%s\n", i, g_argv[i]);
//    }
//    printf("argc: %d\n", g_argc);
//}
//
bool CheckAndExecBuiltin()
{
  std::string cmd = g_argv[0];
  if(cmd == "cd")
  {
    Cd();
    return true;
  }
  return false;
}

int Execute()
{
  pid_t id = fork();
  if(id == 0)
  {
    //child
    execvp(g_argv[0], g_argv);
    exit(1);
  }
  int state = 0;
  //father
  
  pid_t retid = waitpid(id, NULL, 0); 
  if(retid > 0)
  {
    lastnode = WEXITSTATUS(state);
  }
    return 0;
  
}

void InitEnv()
{
  extern char **environ;
    //本来要从配置文件来
    
    //1. 获取环境变量
    
    for(int i = 0; environ[i]; i++)
    {
        // 1.1 申请空间
        g_env[i] = (char*)malloc(strlen(environ[i]) + 1);
        strcpy(g_env[i], environ[i]);
        g_envs++;
    }
    g_env[g_envs++] = (char*)"HAHA=for_test"; //for_test
    g_env[g_envs] = NULL;


    //2. 导成环境变量
    for(int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}


int main()
{
  // shell 启动的时候，从系统中获取环境变量
  // 我们的环境变量信息应该从父shell统一来
    
  InitEnv();
    
  while(1)
  {
      //1.输出命令行指令
      PrintCommdPrompt();
      
      //2. 获得用户输入的指令
      char commandline[COMMAND_SIZE];
      if(!GetCommandLine(commandline, sizeof(commandline)))
      continue;
  
      //printf("echo: %s", commandline);
      //3. 命令行分析"ls -a -l" -> "ls" "-a" "-l"
      if(!CommandParse(commandline))
      continue;
     // PrintArgv();

      //检测别名 
      //4.检查并处理哦 内建命令
      if(CheckAndExecBuiltin())
      continue;

      //4. 执行命令
      Execute();
  }
  return 0;
}
